export type CanvasLabelType = "box" | "polygon";
/**
 * 方框标签类型
 */
const BoxLabel: CanvasLabelType = "box";
/**
 * 多边形标签类型
 */
const PolygonLabel: CanvasLabelType = "polygon";
/**
 * 标签数据对象
 */
export type CanvasLabelItem = {
  id: number;
  labelTypeIds: number[];
  type: CanvasLabelType;
  data: number[];
};
/**
 * 标签事件类型
 * @property typeChange 标签类型改变事件
 * @property drawEnd 绘制结束事件
 * @property hover 鼠标悬停事件
 * @property scale 缩放事件
 * @property translate 平移事件
 * @property labelTypeChange 标签类型改变事件
 * @property select 选择标签事件
 * @property drawStart 绘制开始事件
 * @property canUse 能否使用标签事件
 * @property drawIng 绘制中事件
 */
export type CanvasEventType =
  | "typeChange"
  | "drawEnd"
  | "drawStart"
  | "drawIng"
  | "canUse"
  | "hover"
  | "scale"
  | "translate"
  | "labelTypeChange"
  | "select";
/**
 * 标签事件对象
 */
export interface CanvasEvent<T> {
  type: CanvasEventType;
  data: T;
}
/**
 * 事件回调
 */
export type CanvasEventCallback = <T>(event?: CanvasEvent<T>) => void;
/**
 * 监听器
 */
export type CanvansListener = (
  e: CanvasEventType,
  callback: CanvasEventCallback
) => void;

/**
 * 当前画布的绘制类型
 */
export type CanvasDrawingType = CanvasLabelType;
// 操作类型
export type CanvasLabelOpType =
  | CanvasLabelType
  | "none"
  | "select"
  | "move"
  | "del";
// 背景图片数据
export type BackImageData = ImageBitmap | ImageData;
// 背景图片
export type BackData = {
  // 水平移动距离
  mx: number;
  // 垂直移动距离
  my: number;
  // 图片宽度
  w: number;
  // 图片高度
  h: number;
  // 根据图片原始大小计算的缩放比例
  zoom: number;
  // 原始图片数据
  data: ImageBitmapSource;
};
/**
 * 标签类型
 * @property name 类型名称
 * @property id 类型唯一标识
 * @property strokeWidth 标签边框宽度
 * @property strokeStyle 标签边框颜色
 * @property fillStyle 标签填充颜色
 * @property fontFillStyle 标签字体颜色
 * @property font 标签字体样式
 */
export type LabelType = {
  name: string;
  id: number;
  strokeWidth: number;
  strokeStyle: string;
  fillStyle: string;
  fontFillStyle: string;
  font: string;
};
export type CanvasLabelSetting = {
  background?: string;
  labelTypeList?: LabelType[];
  showLabelTagType?: "number" | "name" | "id";
  offKbdEvent?: boolean;
  sightBead?: {
    coordinatesColor?: string;
    coordinatesWidth?: number;
    fontColor?: string;
    fontStyle?: string;
  };
  selectStyle?: {
    strokeWidth?: number;
    strokeStyle?: string;
    fillStyle?: string;
    pointFillR?: number;
    pointFillStyle?: string;
  };
  selectIngStyle?: {
    strokeWidth?: number;
    strokeStyle?: string;
    fillStyle?: string;
  };
};
function deepCopy<T>(data: T): T {
  return JSON.parse(JSON.stringify(data));
}

function getLabelData(l: CanvasLabelItem) {
  if (l.type === "box") {
    return [
      l.data[0],
      l.data[1],
      l.data[0] + l.data[2],
      l.data[1],
      l.data[0] + l.data[2],
      l.data[1] + l.data[3],
      l.data[0],
      l.data[1] + l.data[3],
    ];
  } else if (l.type === "polygon") {
    return deepCopy(l.data);
  }
  return [];
}
function copyBD(b: BackData) {
  return {
    mx: b.mx,
    my: b.my,
    zoom: b.zoom,
  };
}
class CanvasLabel {
  // 容器元素
  #el: HTMLElement;
  // 2d上下文
  #ctx: CanvasRenderingContext2D;
  // 画布元素
  #canvas: HTMLCanvasElement;
  #resizeOB: ResizeObserver | null = null;
  #backData: BackData | null = null;
  #setting: CanvasLabelSetting = {};
  #store: {
    mouseIsDown: boolean;
    x: number;
    y: number;
    cx: number;
    cy: number;
    oldOpType: CanvasLabelOpType;
    hiddenLabelTypeIds: number[];
    hiddenLabelIds: number[];
    useLabelTypeId: number[];
  } = {
    mouseIsDown: false,
    x: 0,
    y: 0,
    cx: 0,
    cy: 0,
    oldOpType: "none",
    hiddenLabelTypeIds: [],
    hiddenLabelIds: [],
    useLabelTypeId: [1],
  };
  // 操作类型
  #opType: CanvasLabelOpType = "none";
  // 所有标签数据
  #labelList: CanvasLabelItem[] = [];
  #selectLabelIds: number[] = [];
  // 正在标注的数据
  #labelIngData: CanvasLabelItem | null = null;
  /**
   * 初始化事件回调
   */
  #listeners: { [key in CanvasEventType]?: CanvasEventCallback[] } = {};
  #eventFun: CanvasEventCallback = () => {};
  constructor(
    el: string | HTMLElement | HTMLCanvasElement,
    setting?: CanvasLabelSetting,
    watchResize?: boolean
  ) {
    // 检查 el 类型
    let canvas: HTMLCanvasElement;
    if (typeof el === "string") {
      this.#el = document.querySelector(el) as HTMLElement ;
      canvas = document.createElement("canvas");
      this.#el?.appendChild(canvas);
    } else if (el instanceof HTMLElement) {
      this.#el = el;
      canvas = document.createElement("canvas");
      this.#el?.appendChild(canvas);
    } else if ((el as HTMLCanvasElement) instanceof HTMLCanvasElement) {
      this.#el = el as HTMLCanvasElement;
      canvas = el as HTMLCanvasElement;
    } else {
      throw new Error(
        "el参数类型错误 必须是css选择器字符串或 HTMLElement 或 HTMLCanvasElement"
      );
    }
    // 兼容性处理
    if (!canvas.getContext) {
      throw new Error("你的浏览器不支持canvas");
    }
    canvas.style.userSelect = "none";
    this.#canvas = canvas;
    this.#ctx = canvas.getContext("2d")!;
    // 隐藏鼠标
    canvas.style.cursor = "none";
    this.#el.style.cursor = "none";
    // 设置画布大小
    this.reSize();
    // 设置窗口大小监听器
    if (watchResize) {
      let resizeob = new ResizeObserver(() => {
        this.reSize();
      });
      this.#resizeOB = resizeob;
      resizeob.observe(this.#el);
    } else {
      this.reSize();
    }
    // 加载设置
    if(setting){
        this.setSetting(setting);
    }else{
        this.setSetting();
    }

    this.#onEvent();
  }
  /**
   * 重置画布大小
   * @param w
   * @param h
   */
  reSize(w?: number, h?: number): void {
    if (this.#canvas && this.#el) {
      if (w !== undefined && h !== undefined) {
        this.#canvas.width = w;
        this.#canvas.height = h;
      } else {
        this.#canvas.width = this.#el.clientWidth;
        this.#canvas.height = this.#el.clientHeight;
      }
    }
    this.#drawPipeLine();
  }
  /**
   * 重绘
   */
  reDraw() {
    this.#drawPipeLine();
  }
  /**
   * 删除标签
   * @param id 标签id
   */
  delLabel(id: number | number[]) {
    const fun = (id: number) => {
      for (let i = 0; i < this.#labelList.length; i++) {
        if (this.#labelList[i].id === id) {
          this.#labelList.splice(i, 1);
        }
      }
    };
    if (Array.isArray(id)) {
      id.forEach((d) => {
        fun(d);
      });
    } else if (typeof id === "number") {
      fun(id);
    }
    this.#drawPipeLine();
    // this.#drawLocation()
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 添加设置
   * @param st 设置
   */
  setSetting(st?: CanvasLabelSetting) {
    this.#setting.background = st?.background || "#bcffc3";
    this.#setting.showLabelTagType = st?.showLabelTagType || "name";
    this.#setting.labelTypeList = st?.labelTypeList || [
      {
        name: "标签1",
        id: 1,
        strokeWidth: 2,
        strokeStyle: "yellow",
        fillStyle: "#0f04",
        fontFillStyle: "yellow",
        font: "15px '微软雅黑'",
      },
    ];
    this.#setting.sightBead = {
      coordinatesColor: st?.sightBead?.coordinatesColor || "#888",
      coordinatesWidth: st?.sightBead?.coordinatesWidth || 2,
      fontColor: st?.sightBead?.fontColor || "#ff6",
      fontStyle: st?.sightBead?.fontStyle || '10px "微软雅黑"',
    };
    this.#setting.selectStyle = {
      strokeStyle: st?.selectStyle?.strokeStyle || "rgba(200,0,0,0.8)",
      strokeWidth: st?.selectStyle?.strokeWidth || 6,
      fillStyle: st?.selectStyle?.fillStyle || "rgba(122,0,0,.4)",
      pointFillR: st?.selectStyle?.pointFillR || 6,
      pointFillStyle: st?.selectStyle?.pointFillStyle || "rgba(0,230,25,.9)",
    };
    this.#setting.selectIngStyle = {
      strokeStyle: st?.selectStyle?.strokeStyle || "rgba(100,60,0,0.8)",
      strokeWidth: st?.selectStyle?.strokeWidth || 6,
      fillStyle: st?.selectStyle?.fillStyle || "rgba(200,200,0,.4)",
    };
    this.#setting.offKbdEvent = st?.offKbdEvent || false;
    this.#drawPipeLine();
  }
  /**
   * 获取设置
   *
   */
  getSetting() {
    return this.#setting;
  }
  /**
   * 使用某个标签
   * @param id
   */
  useLabelType(id: number[]) {
    this.#store.useLabelTypeId = id;
    this.#eventFun({
      type: "labelTypeChange",
      data: deepCopy(this.#store.useLabelTypeId),
    });
  }
  /**
   *
   * 获取现在正在使用的标签
   */
  getUseLabelType(): number[] {
    return deepCopy(this.#store.useLabelTypeId);
  }
  onEvent(cbk: CanvasEventCallback) {
    this.#eventFun = cbk;
  }
  offEvent() {
    this.#eventFun = () => {};
  }
  /**
   * 设置需要隐藏的标签列表
   * @param id
   */
  setHiddenLabels(ids: number[]) {
    this.#store.hiddenLabelIds = ids;
    this.#drawPipeLine();
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 删除被隐藏的标签列表中的某个标签
   * @param id
   */
  delHiddenLabel(id: number) {
    let index = this.#store.hiddenLabelIds.indexOf(id);
    this.#store.hiddenLabelIds.splice(index, 1);
    this.#drawPipeLine();
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 添加需要隐藏的标签
   * @param id
   */
  addHiddenLabel(id: number) {
    this.#store.hiddenLabelIds.push(id);
    this.#drawPipeLine();
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 切换标签的 显示状态
   * @param id
   */
  checkHiddenLabel(id: number) {
    if (this.labelIsHidden(id)) {
      this.delHiddenLabel(id);
    } else {
      this.addHiddenLabel(id);
    }
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 设置需要隐藏的标签类型列表
   * @param id
   */
  setHiddenLabelTypes(ids: number[]) {
    this.#store.hiddenLabelTypeIds = ids;
    this.#drawPipeLine();
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 删除需要已经隐藏的标签类型
   * @param id
   */
  delHiddenLabelType(id: number) {
    let i = this.#store.hiddenLabelTypeIds.indexOf(id);
    this.#store.hiddenLabelTypeIds.splice(i, 1);
    this.#drawPipeLine();
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 添加需要隐藏的标签类型
   * @param id
   */
  addHiddenLabelType(id: number) {
    this.#store.hiddenLabelTypeIds.push(id);
    this.#drawPipeLine();
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 切换标签类型的显示状态
   * @param id
   */
  checkHiddenLabelType(id: number) {
    if (this.labelTypeIsHidden(id)) {
      this.delHiddenLabelType(id);
    } else {
      this.addHiddenLabelType(id);
    }
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 检查某个标签是否被隐藏
   * @param id
   * @returns
   */
  labelIsHidden(id: number) {
    return this.#store.hiddenLabelIds.indexOf(id) >= 0;
  }
  /**
   * 检查某个标签类型是否被隐藏
   * @param id
   * @returns
   */
  labelTypeIsHidden(id: number) {
    return this.#store.hiddenLabelTypeIds.indexOf(id) >= 0;
  }

  /**
   * 检查标签类型是否正在使用
   * @param id type id
   * @returns 返回 标签类型的使用状态
   */
  labelTypeIsUse(id: number) {
    return this.#store.useLabelTypeId.indexOf(id) >= 0;
  }
  /**
   * 绘制管线
   */
  #drawPipeLine() {
    if (this.#ctx && this.#canvas) {
      let ctx = this.#ctx;
      let cvs = this.#canvas;
      ctx.save();
      this.clear();
      ctx.fillStyle = this.#setting?.background || "#fff";
      ctx.fillRect(0, 0, cvs.width, cvs.height);
      ctx.restore();
      ctx.save();
      if (this.#backData) {
        let d = this.#backData;
        ctx.transform(d.zoom, 0, 0, d.zoom, d.mx, d.my);
        ctx.drawImage(d.data as unknown as CanvasImageSource, 0, 0, d.w, d.h);
        let line = this.#labelList;
        line.forEach((item, index) => {
          this.#drawLabel(item, index + 1);
        });
        let labeling = this.#labelIngData;
        if (labeling) {
          this.#drawLabel(labeling, 0);
        }
      }
      this.#drawPoint();
      ctx.restore();
    }
  }
  /* 获取绘制上下文 */
  getCtx() {
    return this.#ctx;
  }
  /**
   * 清除指定区域的绘制 如果不传入值的话 就会清除整canvas
   * @param x 坐标
   * @param y 坐标
   * @param w 宽
   * @param h 长
   */
  clear(x?: number, y?: number, w?: number, h?: number): void {
    if (!this.#ctx || !this.#canvas) {
      return;
    }
    if (
      x !== undefined &&
      y !== undefined &&
      w !== undefined &&
      h !== undefined
    ) {
      this.#ctx.clearRect(x, y, w, h);
    } else {
      this.#ctx.clearRect(0, 0, this.#canvas.width, this.#canvas.height);
    }
  }
  /**
   * 注册所有的事件
   */
  #onEvent() {
    if (!this.#canvas || !this.#ctx) {
      return;
    }
    const cvs = this.#canvas;
    let that = this;
    function saveMouseP(e: MouseEvent) {
      if (!that.#backData) {
        return;
      }
      // 鼠标坐标
      let x = e.offsetX;
      let y = e.offsetY;
      that.#store.x = x;
      that.#store.y = y;
      let bd = that.#backData;
      // 鼠标坐标 缩放过后再图片上的坐标
      that.#store.cx = (x - bd.mx) / bd.zoom;
      that.#store.cy = (y - bd.my) / bd.zoom;
      let eve = {
        cx: that.#store.cx,
        cy: that.#store.cy,
        x: that.#store.x,
        y: that.#store.y,
      };
      that.#eventFun({ type: "hover", data: eve });
    }
    cvs.addEventListener("wheel", (e) => {
      e.preventDefault();
      if (!this.#canvas || !this.#backData) {
        return;
      }
      let x = e.offsetX;
      let y = e.offsetY;

      if (e.altKey) {
        // 水平移动
        e.deltaY < 0 ? this.translate(20) : this.translate(-20);
      } else if (e.ctrlKey) {
        // 缩放
        e.deltaY < 0 ? this.scale(0.1, x, y) : this.scale(-0.1, x, y);
      } else {
        // 垂直移动
        e.deltaY < 0 ? this.translate(0, 20) : this.translate(0, -20);
      }
      this.#labelNeedSelect("ing");
      this.#drawPipeLine();
      this.#drawLocation();
    });
    cvs.addEventListener("mouseover", (e) => {
      saveMouseP(e);
    });
    cvs.addEventListener("mousemove", (e) => {
      saveMouseP(e);
      this.#labelNeedSelect("ing");
      this.#addDrawing("mousemove");
      this.#selectLabelPoint();
      this.#moveLabelPoint();
      this.#moveSelectLabel(e.movementX, e.movementY);
      this.#drawMove(e.movementX, e.movementY);
      this.#drawLocation();
    });
    cvs.addEventListener("mouseout", () => {
      this.#selectIngLabelIds = [];
      this.#drawPipeLine();
    });
    addEventListener("keydown", this.#keyDownEvent.bind(this));
    addEventListener("keyup", this.#keyUpEvent.bind(this));
    cvs.addEventListener("mousedown", (e) => {
      saveMouseP(e);
      this.#store.mouseIsDown = !this.#store.mouseIsDown;
    });
    cvs.addEventListener("click", () => {
      this.#addDrawing("click");
      this.#labelNeedSelect("entry");
      this.#drawPipeLine();
      this.#drawLocation();
    });
    /* 双击事件 主要用于 提交 闭合 多边形的路径 */
    cvs.addEventListener("dblclick", () => {
      this.#addDrawingEnd("dblclick");
    });
    /* 鼠标抬起时间 主要用于 提交 闭合 方框路径 */
    cvs.addEventListener("mouseup", (e) => {
      saveMouseP(e);
      this.#addDrawingEnd("mouseup");
      this.#store.mouseIsDown = false;
      this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
    });
  }
  /**
   * 键盘按下事件 该事件将被绑定到document
   * @param e 键盘事件对象
   */
  #keyDownEvent(e: KeyboardEvent) {
    if (!this.#canvas) {
      return;
    }
    if (this.#setting.offKbdEvent) {
      return;
    }
    if (e.key === "Shift" && !e.ctrlKey && !e.altKey) {
      e.preventDefault();
      this.checkOpType("move");
    } else if (e.key === "f" || e.key === "F") {
      e.preventDefault();
      this.checkOpType("box");
    } else if (e.key === "d" || e.key === "D") {
      e.preventDefault();
      this.checkOpType("polygon");
    } else if (e.key === "q" || e.key === "Q") {
      e.preventDefault();
      this.checkOpType("select");
    } else if (e.key === "a" || e.key === "K") {
      this.checkOpType("none");
    }
    this.#drawPipeLine();
    this.#drawLocation();
  }
  /**
   * 键盘弹起事件 该事件将被绑定到document
   * @param e 键盘事件对象
   */
  #keyUpEvent(e: KeyboardEvent) {
    if (!this.#canvas) {
      return;
    }
    // const cvs = this.#canvas
    if (e.key === "Shift") {
      this.returnOpType();
      this.#drawPipeLine();
      this.#drawLocation();
    }
  }
  /**
   * 添加点数据
   * @param et 事件类型
   */
  #addDrawing(et: keyof WindowEventMap) {
    if (!this.#backData) {
      return;
    }
    let x = this.#store.cx;
    let y = this.#store.cy;
    if (this.#store.mouseIsDown && this.#opType === "box") {
      /* 有数据才能够进行画框 */
      if (this.#labelIngData) {
        let w = x - this.#labelIngData.data[0];
        let h = y - this.#labelIngData.data[1];
        if (this.#labelIngData.data.length === 4) {
          this.#labelIngData.data.splice(
            this.#labelIngData.data.length - 2,
            2,
            w,
            h
          );
        } else {
          this.#labelIngData.data.push(w, h);
        }
      } else {
        let d: CanvasLabelItem = {
          id: new Date().getTime(),
          /* 数据解构避免浅拷贝影响 */
          labelTypeIds: [...this.#store.useLabelTypeId],
          type: "box",
          data: [],
        };
        d.data.push(x, y);
        this.#labelIngData = d;
      }
    } else if (this.#opType === "polygon") {
      if (et === "click") {
        if (this.#labelIngData) {
          // 如果有数据的话就添加
          this.#labelIngData.data.push(x, y);
        } else {
          // 没有数据则初始化数据
          let d: CanvasLabelItem = {
            id: new Date().getTime(),
            /* 数据解构避免浅拷贝影响 */
            labelTypeIds: [...this.#store.useLabelTypeId],
            type: "polygon",
            data: [],
          };
          d.data.push(x, y);
          this.#labelIngData = d;
        }
      } else if (et === "mousemove") {
        if (this.#labelIngData && this.#labelIngData.data.length > 2) {
          this.#labelIngData.data.splice(
            this.#labelIngData.data.length - 2,
            2,
            x,
            y
          );
        } else if (this.#labelIngData && this.#labelIngData.data.length === 2) {
          this.#labelIngData.data.push(x, y);
        }
      }
    }
  }
  /**
   * 结束绘制
   * @param et 事件类型
   */
  #addDrawingEnd(et: keyof WindowEventMap) {
    if (this.#opType === "box" && et === "mouseup") {
      if (this.#labelIngData) {
        this.#labelList.push(this.#labelIngData);
      }
      this.#labelIngData = null;
    }
    if (this.#opType === "polygon" && et === "dblclick") {
      if (this.#labelIngData) {
        this.#labelIngData.data.splice(this.#labelIngData.data.length - 4, 4);
        this.#labelList.push(this.#labelIngData);
      }
      this.#labelIngData = null;
    }
  }

  /**
   * 添加标注数据
   * @param data
   */
  addLabelData(data: CanvasLabelItem[] | CanvasLabelItem) {
    data = deepCopy(data);
    if (Array.isArray(data)) {
      this.#labelList.push(...data);
    } else {
      this.#labelList.push(data);
    }
    this.#drawPipeLine();
    this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
  }
  /**
   * 获取所有的标签
   */
  getLabelList(): CanvasLabelItem[] {
    return deepCopy(this.#labelList);
  }
  /**
   * 设置标签数据
   * @param ls 标签数组
   */
  setLabelList(ls: CanvasLabelItem[]) {
    if (Array.isArray(ls)) {
      let d = JSON.stringify(ls);
      this.#labelList = JSON.parse(d);
      this.#drawPipeLine();
      this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
    } else {
      throw new Error("setLabelList prama must be Array");
    }
  }
  /**
   * 检查现在的操作类型
   * @returns 操作类型
   */
  getOpType(): CanvasLabelOpType {
    return this.#opType;
  }

  getImageInfo() {
    return deepCopy(this.#backData);
  }
  /**
   * 获取对应标签id 的信息
   * @param ids 标签id列表
   */
  #getLabelSetting(ids: number[]): LabelType[] {
    let r: LabelType[] = [];
    if (this.#setting?.labelTypeList) {
      let ll = this.#setting.labelTypeList;
      for (let l of ll) {
        for (let id of ids) {
          if (id === l.id) {
            r.push(l);
            break;
          }
        }
        if (r.length == ids.length) {
          break;
        }
      }
    }
    return r;
  }
  /**
   * 查询框 是否拥有某标签类型
   * @param lid
   * @param tid
   * @returns
   */
  labelHasLabelType(lid: number, tid: number): boolean {
    for (let label of this.#labelList) {
      if (lid === label.id) {
        return label.labelTypeIds.indexOf(tid) >= 0;
      }
    }
    return false;
  }
  /**
   * 传入数据 自动判断数据是要添加到框的 标签类型还是移除
   * @param lid
   * @param tid
   */
  checkLabelLabelType(lid: number, tid: number) {
    for (let label of this.#labelList) {
      if (lid === label.id) {
        let index = label.labelTypeIds.indexOf(tid);
        if (index >= 0) {
          label.labelTypeIds.splice(index, 1);
          this.#drawPipeLine();
        } else {
          label.labelTypeIds.push(tid);
          this.#drawPipeLine();
        }
        this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
      }
    }
  }
  // 切换 操作类型
  checkOpType(type: CanvasLabelOpType) {
    if (!this.#canvas) {
      return;
    }
    const cvs = this.#canvas;
    if (this.#opType !== type) {
      this.#store.oldOpType = this.#opType;
      if (type !== "move") {
        this.#labelIngData = null;
      }
      this.#opType = type;
      this.#selectIngLabelIds = [];
      this.#canEditPoint = [];
      this.selectLabels([], false);
      if (this.#opType === "move") {
        cvs.style.cursor = "grab";
      } else {
        cvs.style.cursor = "none";
      }
      this.#eventFun({ type: "typeChange", data: this.#opType });
    }
  }
  returnOpType() {
    if (!this.#canvas) {
      return;
    }
    const cvs = this.#canvas;
    this.#opType = this.#store.oldOpType;
    if (this.#opType === "move") {
      cvs.style.cursor = "grab";
    } else {
      cvs.style.cursor = "none";
    }
    this.#eventFun({ type: "typeChange", data: this.#opType });
  }
  /**
   * 缩放图片
   * @param s 添加倍数
   * @param x 相对于图片X轴坐标缩放
   * @param y 相对于图片Y轴坐标缩放
   * @returns
   */
  scale(s: number, x?: number, y?: number) {
    if (!this.#backData || !this.#canvas) {
      return;
    }
    let oz = this.#backData.zoom;
    // 若是缩小的时候 缩放比例 已经很小 就不缩小
    if (s < 0 && oz + s < 0.1) {
      this.#drawPipeLine();
      return;
    }
    if (x === undefined || y === undefined) {
      x = this.#canvas.width / 2;
      y = this.#canvas.height / 2;
    }
    let nz = (this.#backData.zoom += s);
    let mx = this.#backData.mx;
    let my = this.#backData.my;
    this.#backData.mx = mx - ((x - mx) * (nz / oz) - (x - mx));
    this.#backData.my = my - ((y - my) * (nz / oz) - (y - my));
    this.#eventFun({ type: "scale", data: copyBD(this.#backData) });
    this.#drawPipeLine();
  }
  /**
   * 移动图像
   * @param x 水平
   * @param y 垂直
   */
  translate(x: number = 0, y: number = 0) {
    if (!this.#backData) {
      return;
    }
    this.#backData.my += y;
    this.#backData.mx += x;
    this.#eventFun({ type: "translate", data: copyBD(this.#backData) });
    this.#drawPipeLine();
  }
  /**
   * 绘制背景图
   */
  drawImage(entry: ImageBitmapSource | string): Promise<null> {
    return new Promise((res, rej) => {
      this.#backData = null;
      this.#labelList = [];
      const setdata = (d: BackImageData) => {
        if (!this.#canvas) {
          return;
        }
        let data = { mx: 0, my: 0, w: d.width, h: d.height, data: d, zoom: 1 };
        let hz = this.#canvas.height / data.h;
        let wz = this.#canvas.width / data.w;
        data.zoom = (hz < wz ? hz : wz) - 0.05;
        data.mx = (this.#canvas.width - data.zoom * data.w) / 2;
        data.my = (this.#canvas.height - data.zoom * data.h) / 2;
        this.#backData = data;
        this.clear();
        this.#drawPipeLine();
        this.#eventFun({ type: "scale", data: copyBD(this.#backData) });
        res(null);
      };
      if (entry instanceof Image) {
        entry.onload = async () => {
          let bm = await createImageBitmap(entry);
          setdata(bm);
        };
        entry.onerror = () => {
          rej();
        };
        try {
          (async function () {
            let bm = await createImageBitmap(entry);
            setdata(bm);
          })();
        } catch {
          rej();
        }
      } else if (entry instanceof ImageBitmap || entry instanceof ImageData) {
        setdata(entry);
      } else if (typeof entry === "string") {
        let img = new Image();
        img.onload = async () => {
          let bm = await createImageBitmap(img);
          setdata(bm);
        };
        img.onerror = () => {
          rej();
        };
        img.src = entry;
      }
    });
  }
  /**
   * 画框
   * @param data 框数据
   * @param fs 填充样式
   * @param ss 描边样式
   */
  #drawBox(d: CanvasLabelItem, index: number) {
    if (!this.#ctx) {
      return;
    }
    let ctx = this.#ctx;
    ctx.save();
    let setting = this.#getLabelSetting(d.labelTypeIds);
    if (setting.length === 0) {
      ctx.restore();
      return;
    } /* 判断是否处于选择状态 */
    if (this.#selectLabelIds.indexOf(d.id) >= 0) {
      ctx.fillStyle = this.#setting.selectStyle?.fillStyle || "#0008";
      ctx.strokeStyle = this.#setting.selectStyle?.strokeStyle || "#000";
      ctx.lineWidth = this.#setting.selectStyle?.strokeWidth || 6;
    } else if (this.#selectIngLabelIds.indexOf(d.id) >= 0) {
      ctx.fillStyle = this.#setting.selectIngStyle?.fillStyle || "#0008";
      ctx.strokeStyle = this.#setting.selectIngStyle?.strokeStyle || "#000";
      ctx.lineWidth = this.#setting.selectIngStyle?.strokeWidth || 6;
    } else {
      ctx.fillStyle = setting?.[0].fillStyle || "#0008";
      ctx.strokeStyle = setting?.[0].strokeStyle || "#000";
      ctx.lineWidth = setting?.[0].strokeWidth || 2;
    }
    ctx.lineCap = "round";
    ctx.lineJoin = "round";
    ctx.fillRect(d.data[0], d.data[1], d.data[2], d.data[3]);
    ctx.strokeRect(d.data[0], d.data[1], d.data[2], d.data[3]);
    ctx.fillStyle = setting?.[0].fontFillStyle || "#0008";
    ctx.font = setting?.[0].font || "15px '微软雅黑'";
    ctx.textAlign = "center";
    let tag: string = "";
    if (this.#setting.showLabelTagType === "name") {
      setting.forEach((st) => {
        tag += st.name + " ";
      });
    } else if (this.#setting.showLabelTagType === "number") {
      tag = index.toString();
    } else if (this.#setting.showLabelTagType === "id") {
      tag = d.id?.toString() || "无id";
    }
    tag = tag.trim();
    ctx.fillText(
      tag || "未定义标签",
      (d.data[0] + d.data[0] + d.data[2]) / 2,
      d.data[1] - 5
    );
    ctx.restore();
    if (this.#selectLabelIds.indexOf(d.id) >= 0) {
      let l = getLabelData(d);
      for (let i = 0; i < l.length / 2; i++) {
        this.#dpi(
          ctx,
          l[i * 2],
          l[i * 2 + 1],
          (this.#setting.selectIngStyle?.strokeWidth || 6) / 2
        );
      }
    }
  }
  /**
   * 绘制路径点不填充渲染
   * @param ctx 2d 上下文
   * @param d 点数据
   */
  #dp(ctx: CanvasRenderingContext2D, d: CanvasLabelItem) {
    ctx.beginPath();
    let l = d.data.length / 2;
    let minx = 0;
    let miny = 0;
    let maxx = 0;
    for (let i = 0; i < l; i++) {
      if (i == 0) {
        ctx.moveTo(d.data[0], d.data[1]);
        maxx = minx = d.data[0];
        miny = d.data[1];
      } else {
        let ai = i * 2;
        if (d.data[ai] < minx) {
          minx = d.data[ai];
        } else if (d.data[ai] > maxx) {
          maxx = d.data[ai];
        }
        if (d.data[ai + 1] < miny) {
          miny = d.data[ai + 1];
        }
        ctx.lineTo(d.data[ai], d.data[ai + 1]);
      }
    }
    ctx.closePath();
    return { minx, miny, maxx };
  }
  /**
   *
   * @param d 绘制参数
   * @param index
   * @returns
   */
  #drawPath(d: CanvasLabelItem, index: number) {
    if (!this.#ctx) {
      return;
    }
    let ctx = this.#ctx;
    ctx.save();
    let st = this.#getLabelSetting(d.labelTypeIds);
    if (st.length === 0) {
      ctx.restore();
      return;
    }
    // 绘制路径不渲染
    let { minx, miny, maxx } = this.#dp(ctx, d);
    // 加载设置
    if (this.#selectLabelIds.indexOf(d.id) >= 0) {
      ctx.fillStyle = this.#setting.selectStyle?.fillStyle || "#0008";
      ctx.strokeStyle = this.#setting.selectStyle?.strokeStyle || "#000";
      ctx.lineWidth = this.#setting.selectStyle?.strokeWidth || 6;
    } else if (this.#selectIngLabelIds.indexOf(d.id) >= 0) {
      ctx.fillStyle = this.#setting.selectIngStyle?.fillStyle || "#0008";
      ctx.strokeStyle = this.#setting.selectIngStyle?.strokeStyle || "#000";
      ctx.lineWidth = this.#setting.selectIngStyle?.strokeWidth || 6;
    } else {
      ctx.fillStyle = st?.[0].fillStyle || "#0008";
      ctx.strokeStyle = st?.[0].strokeStyle || "#000";
      ctx.lineWidth = st?.[0].strokeWidth || 2;
    }
    ctx.lineCap = "round";
    ctx.lineJoin = "round";
    ctx.stroke();
    ctx.fill();
    ctx.fillStyle = st?.[0].fontFillStyle || "#0008";
    ctx.font = st?.[0].font || "15px '微软雅黑'";
    ctx.textAlign = "center";
    let tag: string = "";
    if (this.#setting.showLabelTagType === "name") {
      st.forEach((s) => {
        tag += s.name + " ";
      });
    } else if (this.#setting.showLabelTagType === "number") {
      tag = index.toString();
    } else if (this.#setting.showLabelTagType === "id") {
      tag = d.id?.toString() || "无id";
    }
    tag = tag.trim();
    ctx.fillText(tag || "未定义标签", (maxx + minx) / 2, miny - 5);
    ctx.restore();
    if (this.#selectLabelIds.indexOf(d.id) >= 0) {
      let l = getLabelData(d);
      for (let i = 0; i < l.length / 2; i++) {
        this.#dpi(
          ctx,
          l[i * 2],
          l[i * 2 + 1],
          (this.#setting.selectIngStyle?.strokeWidth || 6) / 2
        );
      }
    }
  }
  /**
   * 绘制点位
   * @returns
   */
  #dpi(ctx: CanvasRenderingContext2D, x: number, y: number, r: number) {
    ctx.save();
    ctx.beginPath();
    ctx.arc(x, y, r, 0, 2 * Math.PI);
    ctx.fillStyle = this.#setting.selectStyle?.pointFillStyle || "black";
    ctx.fill();
    ctx.closePath();
    ctx.restore();
  }
  /**
   *
   * 绘制编辑点位
   */
  #drawPoint() {
    if (!this.#ctx || !this.#backData) {
      return;
    }
    if (this.#canEditPoint.length === 0) {
      return;
    }
    let d = this.#backData;
    let ctx = this.#ctx;
    let r = (this.#setting.selectStyle?.pointFillR || 6) / d.zoom;
    if (r < (this.#setting.selectStyle?.pointFillR || 6)) {
      r = this.#setting.selectStyle?.pointFillR || 6;
    }
    let x = this.#canEditPoint[1];
    let y = this.#canEditPoint[2];
    this.#dpi(ctx, x, y, r);
  }
  /**
   * 绘制十字准星
   * @param param
   */
  #drawLocation() {
    if (!this.#canvas || !this.#ctx || this.#opType === "move") {
      return;
    }
    let x = this.#store.x;
    let y = this.#store.y;
    let cx = this.#store.cx;
    let cy = this.#store.cy;
    const cvs = this.#canvas;
    const ctx = this.#ctx;
    ctx.save();
    ctx.lineWidth = this.#setting?.sightBead?.coordinatesWidth || 2;
    ctx.strokeStyle = this.#setting?.sightBead?.coordinatesColor || "#fff7";
    ctx.beginPath();
    ctx.moveTo(x, 0);
    ctx.lineTo(x, cvs.height);
    ctx.closePath();
    ctx.moveTo(0, y);
    ctx.lineTo(cvs.width, y);
    ctx.globalCompositeOperation = "difference";
    ctx.stroke();
    ctx.restore();
    ctx.save();
    ctx.font = this.#setting?.sightBead?.fontStyle || '10 "微软雅黑"';
    ctx.fillStyle = this.#setting?.sightBead?.fontColor || "#fff";
    ctx.textAlign = "center";
    ctx.globalCompositeOperation = "difference";
    // 绘制坐标文本
    ctx.fillText(
      `x = ${cx.toFixed(2)}px , y = ${cy.toFixed(2)}px`,
      x - 100,
      y - 8
    );
    ctx.fillText(
      `x = ${cx.toFixed(2)}px , y = ${cy.toFixed(2)}px`,
      x + 100,
      y - 8
    );
    ctx.restore();
  }
  /**
   * 移动画布
   * @param x
   * @param y
   */
  #drawMove(x: number, y: number) {
    if (this.#opType !== "move") {
      this.#drawPipeLine();
      return;
    }
    this.translate(x, y);
  }
  /**
   *
   * @param l 绘画数据
   * @param index 数据的index
   * @returns
   */
  #drawLabel(l: CanvasLabelItem, index: number) {
    if (this.#labelNeedHidden(l)) {
      return;
    }
    if (l.type === "box") {
      this.#drawBox(l, index);
    } else if (l.type === "polygon") {
      this.#drawPath(l, index);
    }
  }
  /**
   * 检查标签需不需要被隐藏
   * @param l 标签信息
   * @returns
   */
  #labelNeedHidden(l: CanvasLabelItem) {
    if (this.labelIsHidden(l.id)) {
      return true;
    }
    for (let id of l.labelTypeIds) {
      if (this.labelTypeIsHidden(id)) {
        return true;
      }
    }
    return false;
  }

  #selectIngLabelIds: number[] = [];
  /**
   * 检查路径是否被选择 并且将被选择的id 放入到 选择列表
   * @returns
   */
  #labelNeedSelect(et: "entry" | "ing") {
    if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
      return;
    }
    // 检查现在是否在编辑点 如果是的话 就不用进行选择
    if (this.#canEditPoint.length > 0) {
      return;
    }
    const ls = this.#labelList;
    let ctx = this.#ctx;
    ctx.save();
    let d = this.#backData;
    ctx.transform(d.zoom, 0, 0, d.zoom, d.mx, d.my);
    this.#selectIngLabelIds = [];
    for (let i = ls.length - 1; i >= 0; i--) {
      ctx.save();
      if (this.#labelNeedHidden(ls[i])) {
        ctx.restore();
        continue;
      }
      if (ls[i].type === "box") {
        // 绘制矩形 不渲染
        ctx.rect(ls[i].data[0], ls[i].data[1], ls[i].data[2], ls[i].data[3]);
      } else if (ls[i].type === "polygon") {
        // 绘制路径不渲染
        this.#dp(ctx, ls[i]);
      }
      // 检查坐标是否在路径内
      if (ctx.isPointInPath(this.#store.x, this.#store.y)) {
        if (et === "ing") {
          this.#selectIngLabelIds = [ls[i].id];
        } else if (et === "entry") {
          this.selectLabels([ls[i].id], false);
        }
        ctx.restore();
        ctx.restore();
        return;
      }
      ctx.restore();
    }
    ctx.restore();
  }
  /**
   * 选择某些标签
   * @param ids 需要选中的标签
   */
  selectLabels(ids: number[], d = true) {
    this.#selectLabelIds = [...ids];
    this.#eventFun({ type: "select", data: deepCopy(this.#selectLabelIds) });
    if (d) {
      this.#drawPipeLine();
    }
  }

  /**
   * 获取被选择的所有标签ids
   * @returns 所有被选择的id
   */
  getSelectLabelsIds() {
    return deepCopy(this.#selectLabelIds);
  }
  /**
   * 查询标签是否现在处于被选择状态
   * @param id 标签id
   * @returns
   */
  labelIsSelect(id: number) {
    return this.#selectLabelIds.indexOf(id) >= 0;
  }
  // 可以编辑的点
  #canEditPoint: number[] = [];
  // 检测鼠标位置是否在某个周围
  #atPointRound(
    ctx: CanvasRenderingContext2D,
    l: CanvasLabelItem,
    d: BackData,
    draw = false
  ) {
    //  将标签数据取出转换为number[]
    let p: number[] = getLabelData(l);
    let r = (this.#setting.selectStyle?.pointFillR || 6) / d.zoom;
    if (r < (this.#setting.selectStyle?.pointFillR || 6)) {
      r = this.#setting.selectStyle?.pointFillR || 6;
    }
    for (let i = 0; i < p.length / 2; i++) {
      ctx.save();
      ctx.beginPath();
      ctx.arc(p[i * 2], p[i * 2 + 1], r, 0, 2 * Math.PI);
      ctx.closePath();
      if (ctx.isPointInPath(this.#store.x, this.#store.y)) {
        this.#canEditPoint = [i, p[i * 2], p[i * 2 + 1]];
        this.#selectIngLabelIds = [];
        ctx.restore();
        return i;
      }
      ctx.restore();
    }
    return false;
  }
  /**
   * 选择某个点
   * @returns
   */
  #selectLabelPoint() {
    if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
      return;
    }
    // 检查现在是否处于多选状态 或无选择 是的话就不能进行点位选择
    if (this.#selectLabelIds.length > 1 || this.#selectLabelIds.length == 0) {
      return;
    }
    if (this.#store.mouseIsDown) {
      return;
    }
    // 重置当前选择的点
    this.#canEditPoint = [];
    for (let l of this.#labelList) {
      if (this.#selectLabelIds[0] === l.id) {
        if (this.#labelNeedHidden(l)) {
          return;
        }
        let ctx = this.#ctx;
        ctx.save();
        let d = this.#backData;
        ctx.transform(d.zoom, 0, 0, d.zoom, d.mx, d.my);
        this.#atPointRound(ctx, l, d);
        ctx.restore();
        return;
      }
    }
  }
  /**
   * 原生移动标签坐标
   * @param x
   * @param y
   * @returns
   */
  #moveSelectLabel(x: number, y: number) {
    if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
      return;
    }
    // 检查现在是否是在移动点位 有没有被选择的框
    if (
      !this.#store.mouseIsDown ||
      this.#selectLabelIds.length === 0 ||
      this.#canEditPoint.length > 0
    ) {
      return;
    }
    let b = this.#backData;
    x = x / b.zoom;
    y = y / b.zoom;
    this.moveSelectLabels(x, y, false);
  }
  /**
   * 移动某个标签的某个点
   * @returns
   */
  #moveLabelPoint() {
    if (this.#opType !== "select" || !this.#ctx || !this.#backData) {
      return;
    }
    if (!this.#store.mouseIsDown) {
      return;
    }
    if (this.#selectLabelIds.length == 1 && this.#canEditPoint.length > 0) {
      this.#labelList.forEach((l) => {
        if (l.id === this.#selectLabelIds[0]) {
          let cx = this.#store.cx;
          let cy = this.#store.cy;
          if (l.type === "box") {
            let x = l.data[0];
            let y = l.data[1];
            let w = l.data[2];
            let h = l.data[3];
            // 根据不同点位调整元数据坐标
            switch (this.#canEditPoint[0]) {
              case 0:
                l.data[0] = cx;
                l.data[1] = cy;
                l.data[2] = x + w - cx;
                l.data[3] = y + h - cy;
                break;
              case 1:
                l.data[0] = x;
                l.data[1] = cy;
                l.data[2] = cx - x;
                l.data[3] = y - cy + h;
                break;
              case 2:
                l.data[0] = x;
                l.data[1] = y;
                l.data[2] = cx - x;
                l.data[3] = cy - y;
                break;
              case 3:
                l.data[0] = cx;
                l.data[1] = y;
                l.data[2] = x - cx + w;
                l.data[3] = cy - y;
                break;
            }
          } else if (l.type === "polygon") {
            l.data[this.#canEditPoint[0] * 2] = cx;
            l.data[this.#canEditPoint[0] * 2 + 1] = cy;
          }
          this.#canEditPoint[1] = cx;
          this.#canEditPoint[2] = cy;
        }
      });
    }
  }
  /**
   * 移动某些标签
   * @param x x 方向移动多少
   * @param y y 方向移动多少
   */
  moveSelectLabels(x: number = 0, y: number = 0, o = true) {
    const sLs = this.#selectLabelIds;
    this.#labelList.forEach((l) => {
      if (sLs.indexOf(l.id) >= 0) {
        if (l.type === "box") {
          l.data[0] = l.data[0] + x;
          l.data[1] = l.data[1] + y;
        } else if (l.type === "polygon") {
          let data: number[] = [];
          l.data.forEach((d, i) => {
            if ((i & 1) == 0) {
              data.push(d + x);
            } else {
              data.push(d + y);
            }
          });
          l.data = data;
        }
      }
    });
    if (o) {
      this.#eventFun({ type: "drawEnd", data: deepCopy(this.#labelList) });
    }
  }
  /**
   * 销毁 移除事件绑定
   */
  des() {
    removeEventListener("keydown", this.#keyDownEvent);
    removeEventListener("keyup", this.#keyUpEvent);
    this.#resizeOB?.unobserve(this.#el as Element);
    this.#resizeOB?.disconnect();
  }
}

export { CanvasLabel, deepCopy };
